package com.mask.im.business.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mask.im.business.common.model.PageResult;
import com.mask.im.business.common.model.RestResponse;
import com.mask.im.business.model.entity.chat.Friendships;
import com.mask.im.business.model.entity.UserAuth;
import com.mask.im.business.model.entity.UserInfo;
import com.mask.im.business.model.entity.UserRole;
import com.mask.im.business.common.exception.BusinessException;
import com.mask.im.business.mapper.UserAuthMapper;
import com.mask.im.business.mapper.UserInfoMapper;
import com.mask.im.business.model.dto.*;
import com.mask.im.business.model.vo.*;
import com.mask.im.business.service.*;
import com.mask.im.business.common.util.BeanCopy;
import com.mask.im.business.common.util.MaskIMSecurityUtils;
import com.mask.im.business.common.util.PageUtil;
import com.mask.im.business.common.model.IMSendMessage;
import com.mask.token.util.MaskSecurityUtil;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static com.mask.im.business.common.constant.CommonConstant.FORCED_OFFLINE;
import static com.mask.im.business.common.constant.RedisConstant.USER_LOGIN;
@Slf4j
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

	@Autowired
	private UserAuthMapper userAuthMapper;

	@Autowired
	private ChannelService channelService;

	@Autowired
	private FriendshipsService friendshipsService;

	@Autowired
	private UserRoleService userRoleService;

	@Autowired
	private RedisService redisService;

	@Autowired
	private TokenService tokenService;

	@Override
	public RestResponse<?> getUserInfo() {
		Integer id = MaskIMSecurityUtils.getUserId();
		PersonalDTO personalDTO = this.baseMapper.getUserInfo(id);
		return RestResponse.ok(personalDTO);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public RestResponse<PersonalDTO> saveUserInfo(PersonalDTO personalDTO) {
		PersonalDTO userInfo = this.baseMapper.getUserInfo(personalDTO.getId());
		preventModifications(personalDTO, userInfo);
		UserInfo info = BeanCopy.singleCopy(personalDTO, UserInfo.class);
		this.baseMapper.updateById(info);
		userAuthMapper.update(null, new LambdaUpdateWrapper<UserAuth>()
				.set(UserAuth::getUsername, personalDTO.getUsername())
				.eq(UserAuth::getUserInfoId, personalDTO.getId()));
		PersonalDTO mapperUserInfo = this.baseMapper.getUserInfo(personalDTO.getId());
		return RestResponse.ok(mapperUserInfo);
	}

	@Override
	public RestResponse<String> updateEmailAndPhoneView(Integer id, String type) {
		UserAuth userAuth = userAuthMapper.selectOne(new LambdaQueryWrapper<UserAuth>()
				.select(UserAuth::getPhone, UserAuth::getEmail)
				.eq(UserAuth::getUserInfoId, id));
		if (Objects.isNull(userAuth)) {
			return RestResponse.ok("");
		}
		return RestResponse.ok(type.equalsIgnoreCase("email") ? userAuth.getEmail() : userAuth.getPhone());
	}

	@Override
	public RestResponse<Boolean> userOnlineStatus(Integer userInfo) {
		return RestResponse.ok(channelService.getUserIdChannel(userInfo.toString()) != null);
	}

	@Override
	public RestResponse<PostcardDTO> getByIdPostcardUser(Long userInfoId) {
		Long myId = MaskSecurityUtil.getCurrentUserId();
		UserInfo userInfo = this.baseMapper.selectById(userInfoId);
		PostcardDTO postcardDTO = new PostcardDTO();
		postcardDTO.setId(userInfo.getId());
		postcardDTO.setIntro(userInfo.getIntro());
		postcardDTO.setAvatar(userInfo.getAvatar());
		postcardDTO.setGender(userInfo.getGender());
		Friendships friendships = friendshipsService.confirmIsFriend(myId, userInfoId);
		if (Objects.isNull(friendships)) {
			Friendships confirmIsFriend = friendshipsService.confirmIsFriend(userInfoId, myId);
			postcardDTO.setUserName(userInfo.getNickName());
			postcardDTO.setIsFriend(confirmIsFriend==null?2:confirmIsFriend.getIsFriends());
			return RestResponse.ok(postcardDTO);
		}
		postcardDTO.setUserName(friendships.getRemark());
		postcardDTO.setIsFriend(friendships.getIsFriends());
		return RestResponse.ok(postcardDTO);
	}

	private void preventModifications(PersonalDTO personalDTO, PersonalDTO userInfo) {
		if (Objects.isNull(userInfo.getEmail())) {
			if (personalDTO.getEmail() != null) {
				throw new BusinessException("修改失败");
			}
		} else {
			if (!userInfo.getEmail().equals(personalDTO.getEmail())) {
				throw new BusinessException("修改失败");
			}
		}
		if (Objects.isNull(userInfo.getPhone())) {
			if (personalDTO.getPhone() != null) {
				throw new BusinessException("修改失败");
			}
		} else {
			if (!userInfo.getPhone().equals(personalDTO.getPhone())) {
				throw new BusinessException("修改失败");
			}
		}

	}


	@Transactional(rollbackFor = Exception.class)
	@Override
	public void updateUserRole(UserRoleVO userRoleVO) {
		UserInfo userInfo = UserInfo.builder()
				.id(userRoleVO.getUserInfoId())
				.nickName(userRoleVO.getNickname())
				.build();
		this.baseMapper.updateById(userInfo);
		userRoleService.remove(new LambdaQueryWrapper<UserRole>()
				.eq(UserRole::getUserInfoId, userRoleVO.getUserInfoId()));
		List<UserRole> userRoleList = userRoleVO.getRoleIds().stream()
				.map(roleId -> UserRole.builder()
						.roleId(roleId)
						.userInfoId(userRoleVO.getUserInfoId())
						.build())
				.collect(Collectors.toList());
		userRoleService.saveBatch(userRoleList);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void updateUserDisable(UserDisableVO userDisableVO) {
		UserInfo userInfo = UserInfo.builder()
				.id(userDisableVO.getId())
				.disable(userDisableVO.getIsDisable())
				.build();
		this.baseMapper.updateById(userInfo);
	}

	@Override
	public PageResult<UserOnlineDTO> listOnlineUsers(ConditionVO conditionVO) {
		//获取所有用户信息的k-v
		Map<String, Object> userMaps = redisService.hGetAll(USER_LOGIN);
		//获取所有用户的v
		Collection<Object> values = userMaps.values();

		List<UserDetailsDTO> userDetailsDTOs = new ArrayList<>();
		//将数据全部存入userDetailsDTOs
		for (Object value : values) {
			userDetailsDTOs.add((UserDetailsDTO) value);
		}
		//将数据复制到UserOnlineDTO
		List<UserOnlineDTO> userOnlineDTOs = BeanCopy.listCopy(userDetailsDTOs, UserOnlineDTO.class);
		//过滤掉不属于所搜素的用户，并对登录时间降序排序
		List<UserOnlineDTO> onlineUsers = userOnlineDTOs.stream()
				.filter(item -> StringUtils.isBlank(conditionVO.getKeywords()) || item.getNickName().contains(conditionVO.getKeywords()))
				.sorted(Comparator.comparing(UserOnlineDTO::getLastLoginTime).reversed())
				.collect(Collectors.toList());
		//获取当前页数
		int fromIndex = (int) PageUtil.getCurrentPage();
		//获取每页显示的行数
		int size = (int) PageUtil.getSize();
		//当当前数据大于每页显示的行数，则截取userOnlineList第当前页页显示的部分
		int toIndex = onlineUsers.size() - fromIndex > size ? fromIndex + size : onlineUsers.size();
		List<UserOnlineDTO> userOnlineList = onlineUsers.subList(fromIndex, toIndex);
		return PageResult.<UserOnlineDTO>builder().data(userOnlineList).total(onlineUsers.size()).build();
	}

	@Override
	public void removeOnlineUser(Integer userInfoId) {
		tokenService.delLoginUser(userInfoId);
		Channel userIdChannel = channelService.getUserIdChannel(userInfoId.toString());
		if (userIdChannel != null) {
			IMSendMessage websocketDTO = new IMSendMessage();
			websocketDTO.setCmd(40002);
			websocketDTO.setData(FORCED_OFFLINE);
			userIdChannel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO)));
		}
	}

	@Override
	public UserInfoDTO getUserInfoById(Integer id) {
		UserInfo userInfo = this.baseMapper.selectById(id);
		return BeanCopy.singleCopy(userInfo, UserInfoDTO.class);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void updateUserInfo(UserInfoVO userInfoVO) {
		UserInfo userInfo = UserInfo.builder()
				.id(MaskSecurityUtil.getCurrentUserId())
				.nickName(userInfoVO.getNickName())
				.avatar(userInfoVO.getAvatar())
				.intro(userInfoVO.getIntro())
				.build();
		this.baseMapper.updateById(userInfo);
		UserDetailsDTO userDetailsDTO = new UserDetailsDTO();
//                MaskIMSecurityUtils.getUserDetailsDTO();
		userDetailsDTO.setNickName(userInfoVO.getNickName());
		userDetailsDTO.setIntro(userInfoVO.getIntro());
		tokenService.refresh(userDetailsDTO);
	}

	@Override
	public void removeAllOnlineUser(String reason) {
		List<Channel> allOnlineUser = channelService.getAllOnlineUser();
		Channel myChannel = channelService.getUserIdChannel(MaskIMSecurityUtils.getUserId().toString());
		allOnlineUser.remove(myChannel);
		IMSendMessage websocketDTO = new IMSendMessage();
		websocketDTO.setCmd(40002);
		websocketDTO.setData(FORCED_OFFLINE);
		allOnlineUser.forEach(channel -> {
			String userInfoId = channelService.getChannelUserId(channel);
			tokenService.delLoginUser(Integer.valueOf(userInfoId));
			channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO)));
		});
	}
}
